﻿using System;
using System.Web.UI;
using System.Linq;
using Microsoft.SharePoint;
using System.Web.UI.WebControls;

namespace Evolution.UI.WebParts
{
    [Serializable]
    public class TrackAccessToPageAction : BaseAction
    {
        #region Constructors

        public TrackAccessToPageAction()
        {

        }

        public TrackAccessToPageAction(Guid logListGuid, Guid logDataField, LoggingOptions options/*, PageElements pageElements*/)
        {
            this.List = logListGuid;
            this.Field = logDataField;
            this.Options = options;
        }

        #endregion

        #region Properties

        public Guid List { get; set; }

        public Guid Field { get; set; }

        public LoggingOptions Options { get; set; }

        public override string Caption
        {
            get { return "Track access to the page"; }
        }

        #endregion

        #region Methods

        public override void CreateInterface(ControlCollectionBuilder builder, PageActionEditor editor)
        {
            builder.AddStaticText(@"<div class=""UserControlBody"" style=""margin-top: 5px"">List</div>");
            builder.AddStaticText(@"<div class=""UserControlGroup"" style=""padding-bottom: 2px;"">");

            DropDownList logList = new DropDownList();
            logList.ID = "Evolution_Pawp_LogList";
            logList.SelectedIndexChanged += new EventHandler(this.ChangeList);
            logList.Style[HtmlTextWriterStyle.Width] = "175px";
            logList.AutoPostBack = true;
            logList.Items.Add(new ListItem("None", ""));
            logList.ToolTip = "Select the SharePoint list to store the log messages";
            foreach (SPList item in SPContext.Current.Web.Lists)
                if (item.BaseType == SPBaseType.GenericList)
                    logList.Items.Add(new ListItem(item.Title, item.ID.ToString()));

            builder.AddControl(logList);

            builder.AddStaticText("&nbsp;");

            ImageButton image = new ImageButton();
            image.ID = "Evolution_Pawp_Refresh";
            image.BorderStyle = BorderStyle.None;
            image.Width = Unit.Pixel(14);
            image.Height = Unit.Pixel(13);
            image.ImageUrl = "/_layouts/images/Evolution/refresh.gif";
            image.AlternateText = "Refresh list";
            image.Click += new ImageClickEventHandler(this.RefreshList);

            builder.AddControl(image);

            builder.AddStaticText("</div>");

            builder.AddErrorPlaceholder("LogList");

            builder.AddStaticText("<div class=\"UserControlBody\" style=\"margin-top: 5px;\">Log field</div>");
            builder.AddStaticText(@"<div class=""UserControlGroup"" style=""padding-bottom: 2px;"">");

            DropDownList logDataField = new DropDownList();
            logDataField.ID = "Evolution_Pawp_LogDataField";
            logDataField.Style[HtmlTextWriterStyle.Width] = "175px";
            logDataField.Items.Add(new ListItem(" "));
            logDataField.Enabled = false;
            logDataField.ToolTip = "Select the field of the list to store the log message";

            builder.AddControl(logDataField);

            builder.AddStaticText("</nobr></div>");

            builder.AddStaticText("<div class=\"UserControlBody\" style=\"margin-top: 5px;\">Logging options</div>");
            builder.AddStaticText(@"<div class=""UserControlGroup"" style=""padding-bottom: 2px;"">");

            CheckBoxList logOptions = new CheckBoxList();
            logOptions.ID = "Evolution_Pawp_LogOptions";
            logOptions.Style[HtmlTextWriterStyle.Width] = "165px";
            ListItem item1 = new ListItem("Date", "1");
            item1.Selected = true;
            logOptions.Items.Add(item1);
            item1 = new ListItem("Time", "2");
            item1.Selected = true;
            logOptions.Items.Add(item1);
            item1 = new ListItem("User Name", "4");
            item1.Selected = true;
            logOptions.Items.Add(item1);
            logOptions.Items.Add(new ListItem("Page URL", "8"));
            logOptions.Items.Add(new ListItem("Web Part ID", "16"));
            logOptions.Items.Add(new ListItem("QueryString", "32"));
            logOptions.Items.Add(new ListItem("Form", "64"));
            logOptions.Items.Add(new ListItem("Cookie", "128"));
            logOptions.Items.Add(new ListItem("Referer", "256"));
            logOptions.Items.Add(new ListItem("Client IP Address", "512"));
            logOptions.Items.Add(new ListItem("User Agent", "1024"));

            builder.AddControl(logOptions);

            builder.AddErrorPlaceholder("LogOptions");

            builder.AddStaticText("</div>");
        }
       
        public override void Execute(Page page, PageActionWebPart part)
        {
            SPWeb web = SPContext.Current.Web;
            this.Log(web, this.List, this.Field, this.Options, page, part);
        }

        public override string GetDescription()
        {
            return "Track access to the page";
        }

        public override bool IsValidAction(PageActionWebPart part, out string errorMessage)
        {
            errorMessage = String.Empty;

            SPList list = null;
            try { list = SPContext.Current.Web.Lists[this.List]; }
            catch (Exception listException) { errorMessage += "Error accessing the specified list. " + listException.Message; }

            if (list != null && this.Field.CompareTo(Guid.Empty) != 0)
            {
                try { SPField field = list.Fields[this.Field]; }
                catch (Exception fieldException) { errorMessage = (String.IsNullOrEmpty(errorMessage) ? "" : errorMessage + "<br/>") + "Error accessing the specified field. " + fieldException.Message; }
            }

            if (this.Field.CompareTo(Guid.Empty) != 0 && this.Options == LoggingOptions.None)
                errorMessage = (String.IsNullOrEmpty(errorMessage) ? "" : errorMessage + "<br/>") + "No logging option specified";
            
            return String.IsNullOrEmpty(errorMessage);
        }

        public override void ApplyChanges(PageActionEditor editor)
        {
            DropDownList logList = editor.FindControl("Evolution_Pawp_LogList") as DropDownList;
            if (logList.SelectedIndex == 0)
                editor.RaiseError("LogList", "<br/>Specify the list to be used for tracking access.", String.Format("The action '{0}' requires the indication of a list in which to track information about the accesses to the page.", this.Caption));

            DropDownList logDataField = editor.FindControl("Evolution_Pawp_LogDataField") as DropDownList;

            CheckBoxList options = editor.FindControl("Evolution_Pawp_LogOptions") as CheckBoxList;
            LoggingOptions optionFlags = LoggingOptions.None;
            foreach (ListItem item in options.Items)
                if (item.Selected)
                    optionFlags |= (LoggingOptions)Enum.Parse(typeof(LoggingOptions), item.Value);

            if (logDataField.SelectedIndex != 0 && optionFlags == LoggingOptions.None)
                editor.RaiseError("LogOptions", "<br/>Specify at least one of the items that must be recorded in the log.", String.Format("The action '{0}' requires that if you select the Log field, you must specify at least one of the items to be recorded in the log.", this.Caption));

            this.List = new Guid(logList.SelectedValue);
            this.Field = new Guid(logDataField.SelectedValue);
            this.Options = optionFlags;
        }

        public override void SyncChanges(PageActionEditor editor)
        {
            DropDownList logList = editor.FindControl("Evolution_Pawp_LogList") as DropDownList;
            logList.SelectedValue = Convert.ToString(this.List);

            DropDownList logDataField = editor.FindControl("Evolution_Pawp_LogDataField") as DropDownList;
            this.FillFieldList(logList, logDataField);
            logDataField.SelectedValue = Convert.ToString(this.Field);

            CheckBoxList options = editor.FindControl("Evolution_Pawp_LogOptions") as CheckBoxList;
            LoggingOptions opts = this.Options;
            for (int j = 0; j < options.Items.Count; j++)
            {
                LoggingOptions op = (LoggingOptions)Math.Pow(2, j);
                options.Items[j].Selected = (opts & op) == op;
            }
        }

        private void Log(SPWeb web, Guid list, Guid field, LoggingOptions opts, Page page, PageActionWebPart part)
        {
            string user = web.CurrentUser.ID + ";#" + web.CurrentUser.Name;

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(web.Site.ID))
                {
                    using (SPWeb web2 = site.OpenWeb(web.ID))
                    {
                        bool oldValue = web2.AllowUnsafeUpdates;
                        web2.AllowUnsafeUpdates = true;

                        SPList logList = web2.Lists[list];
                        SPListItem item =
                            logList.Items.Add();

                        item["Author"] = user;
                        if (field.CompareTo(Guid.Empty) != 0)
                        {
                            string flags = "";

                            if ((opts & LoggingOptions.Date) == LoggingOptions.Date)
                                flags += DateTime.Now.ToString("yyyy-MM-dd") + "|";

                            if ((opts & LoggingOptions.Time) == LoggingOptions.Time)
                                flags += DateTime.Now.ToString("HH:mm:ss") + "|";

                            if ((opts & LoggingOptions.UserName) == LoggingOptions.UserName)
                                flags += web.CurrentUser.LoginName + "|";

                            if ((opts & LoggingOptions.PageURL) == LoggingOptions.PageURL)
                                flags += page.Request.Url.AbsolutePath + "|";

                            if ((opts & LoggingOptions.WebPartID) == LoggingOptions.WebPartID)
                                flags += part.Title + "." + part.ZoneIndex + "|";

                            if ((opts & LoggingOptions.QueryString) == LoggingOptions.QueryString)
                                flags += page.Request.QueryString.ToString() + "|";

                            if ((opts & LoggingOptions.Form) == LoggingOptions.Form)
                                flags += page.Request.Form.ToString() + "|";

                            if ((opts & LoggingOptions.Cookie) == LoggingOptions.Cookie)
                                flags += (from x in page.Request.Cookies.AllKeys select x + "=" + page.Request.Cookies[x].Value).Aggregate((a, b) => a + "&" + b) + "|";

                            if ((opts & LoggingOptions.Referer) == LoggingOptions.Referer)
                                flags += page.Request.UrlReferrer + "|";

                            if ((opts & LoggingOptions.ClientIPAddress) == LoggingOptions.ClientIPAddress)
                                flags += page.Request.UserHostAddress + "|";

                            if ((opts & LoggingOptions.UserAgent) == LoggingOptions.UserAgent)
                                flags += page.Request.UserAgent + "|";

                            item[field] = flags.Length > 0 ? flags.Substring(0, flags.Length - 1) : String.Empty;
                        }

                        item.Update();

                        web2.AllowUnsafeUpdates = oldValue;
                    }
                }
            });
        }

        public void ChangeList(object sender, EventArgs e)
        {
            DropDownList logList = sender as DropDownList;
            DropDownList logDataField = logList.Parent.FindControl("Evolution_Pawp_LogDataField") as DropDownList;

            this.FillFieldList(logList, logDataField);
        }

        public void RefreshList(object sender, ImageClickEventArgs e)
        {
            SPWeb web = SPContext.Current.Web;
            ImageButton button = sender as ImageButton;

            DropDownList logList = button.Parent.FindControl("Evolution_Pawp_LogList") as DropDownList;
            DropDownList logDataField = button.Parent.FindControl("Evolution_Pawp_LogDataField") as DropDownList;

            if (logList != null || logDataField != null)
            {
                logList.Items.Clear();
                logList.Items.Add(new ListItem("None", ""));
                foreach (SPList item in web.Lists)
                    if (item.BaseType == SPBaseType.GenericList)
                        logList.Items.Add(new ListItem(item.Title, item.ID.ToString()));

                logDataField.Items.Clear();
                logDataField.Enabled = false;
            }
        }

        public void FillFieldList(DropDownList logList, DropDownList logDataField)
        {
            if (logList == null || logDataField == null)
                throw new InvalidCastException();

            if (logList.SelectedValue == String.Empty)
            {
                logDataField.Items.Clear();
                logDataField.Enabled = false;
                return;
            }

            SPWeb web = SPContext.Current.Web;
            SPList list;

            try { list = web.Lists[new Guid(logList.SelectedValue)]; }
            catch { return; };

            logDataField.Items.Clear();
            logDataField.Items.Add(new ListItem("", Guid.Empty.ToString()));
            foreach (SPField field in list.Fields)
                if (field as SPFieldMultiLineText != null)
                    logDataField.Items.Add(new ListItem(field.Title, field.Id.ToString()));

            logDataField.Enabled = true;
        }

        #endregion
    }

    #region LoggingOptions enum

    [Flags]
    public enum LoggingOptions
    {
        None = 0,
        Date = 1,
        Time = 2,
        UserName = 4,
        PageURL = 8,
        WebPartID = 16,
        QueryString = 32,
        Form = 64,
        Cookie = 128,
        Referer = 256,
        ClientIPAddress = 512,
        UserAgent = 1024
    }

    #endregion
}
